#pragma once

#include<iostream>
#include<vector>
#include<queue>
// #include<string>
#include "thread.hpp"
#include "lockGuard.hpp"
#include "log.hpp"

//本质是生产消费模型

const int g_thread_num = 3;

template<class T>
class ThreadPool
{
public:
    pthread_mutex_t *getMutex()
    {
        return &lock;
    }
    bool isEmpty()
    {
        return _task_queue.empty();
    }
    void waitCond()
    {
        pthread_cond_wait(&cond, &lock);
    }

    T getTask()
    {
        T t = _task_queue.front();
        _task_queue.pop();
        return t;
    }

private:
    ThreadPool(int thread_num = g_thread_num):_num(thread_num)
    {
        pthread_mutex_init(&lock,nullptr);
        pthread_cond_init(&cond, nullptr);
        
        for(int i = 1;i<=_num;i++)
        {
            _threads.push_back(new Thread(i, routine, this));   //注意这个this指针，这样线程就能够访问线程池了！！
        }
    }

    //禁止线程池进行拷贝和赋值
    ThreadPool(const ThreadPool<T> &other) = delete;
    const ThreadPool<T>& operator=(const ThreadPool<T> &other) = delete;
public:
    //考虑一下多线程使用单例的过程
    static ThreadPool<T>* getThreadPool(int num = g_thread_num)
    {
        //可以有效减少未来必定要进行加锁检测的问题
        //拦截大量的已经创建好单例的时候，剩余线程请求单例的而直接访问锁的行为
        if(nullptr == _thread_ptr)
        {
            lockGuard lockguard(&mutex);
            //但是，未来任何一个线程想获取单例，都必须调用getThreadPool接口，因为构造私有化了，所以必须也得static，使用类域访问
            if(nullptr == _thread_ptr)
            {
                _thread_ptr = new ThreadPool<T>(num);  //.....
            }
        }

        return _thread_ptr;
    }

    // 1. run
    void run()
    {
        for(auto &it:_threads)
        {
            it->start();
            //std::cout << iter->name() << " 启动成功" << std::endl;
            logMessage(NORMAL, "%s %s", it->name().c_str(), "启动成功");    
        }
    }

    // 线程池本质也是一个生产消费模型
    // void* routine(void *args)
    // 消费过程
    static void *routine(void *args)    //因为线程传进的函数，不能有this参数(加上就是两个)，所以需要设置为static,这里的routine的args是thread里的_data，
                                        //而_data的类型是ThreadData，并且Data里面存的args是线程池的指针
    {
        ThreadData *td = (ThreadData*)args;      //先获得threadData
        ThreadPool<T> *tp = (ThreadPool<T> *)td->_args;   //获得了this指针

        while(true)
        {
            T task;
            {
                lockGuard lockguard(tp->getMutex());    //静态的成员函数因为没有this指针，不能直接访问类的非静态数据，但是因为tp就是this指针，所以不用这个函数也可以完成任务
                while(tp->isEmpty()) tp->waitCond();    // 如果任务队列（缓冲区为空，挂起）

                //读取任务
                task = tp->getTask();    //队列是共享的->将任务从共享，拿到自己的私有空间
            }
            task(td->_name);   //打印出自己已经完成了
        }
    }

    void pushTask(const T&task)
    {
        lockGuard lockguard(&lock);
        _task_queue.push(task);
        pthread_cond_signal(&cond);   //有任务了就唤醒
    }

    ~ThreadPool()
    {
        for(auto &it:_threads)
        {
            it->join();
            delete it;
        }
        pthread_mutex_destroy(&lock);
        pthread_cond_destroy(&cond);
    }


private:
    std::vector<Thread *> _threads;    //指向线程
    int _num;     //线程池的线程数
    std::queue<T> _task_queue;

    static ThreadPool<T> *_thread_ptr;    //指向线程池的指针
    static pthread_mutex_t mutex;         //创建线程池的互斥锁

    pthread_mutex_t lock;    //是信号量的锁和信号
    pthread_cond_t cond;
};


// 静态变量需要在类外初始化,线程池只需要一个，指向线程池的指针初始化为nullptr
// 线程池的锁也需要初始化
template<typename T>
ThreadPool<T> *ThreadPool<T>::_thread_ptr = nullptr;

template<class T>
pthread_mutex_t ThreadPool<T>::mutex = PTHREAD_MUTEX_INITIALIZER;